/*
 * File: AuthorizeManager.java
 * Author: Force Charlie
 * CreateTime: @2015.11
 * Copyright (C) 2018. GITEE.COM. All Rights Reserved.
 */

package svnserver.service;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.InputStream;

public class AuthorizeManager {
  private static final org.slf4j.Logger log = LoggerFactory.getLogger(AuthorizeManager.class);
  private static PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
  private final static int DEFAULT = 64;

  static {
    manager.setMaxTotal(DEFAULT);
    manager.setDefaultMaxPerRoute(DEFAULT);
  }

  private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
    SSLConnectionSocketFactory sslsf = null;
    try {
      SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (certificate, authType) -> true).build();
      sslsf = new SSLConnectionSocketFactory(sslContext);
    } catch (Exception e) {
      log.error("createSSLConnSocketFactory", e);
    }
    return sslsf;
  }

  static public String DiskSize(long size) {
    if (size > 1024 * 1024 * 1024) {
      return String.format("%.2f GB", (double) size / 1024 / 1024 / 1024);
    }
    if (size > 1024 * 1024) {
      return String.format("%.2f MB", (double) size / 1024 / 1024);
    }
    if (size > 10 * 1024) {
      return String.format("%.2f KB", (double) size / 10240);
    }
    if (size == 0) {
      return "No size details";
    }
    return String.format("%d Byte", size);
  }

  /// JSON
  private static String HttpAuthorizePost(@NotNull String url, @NotNull String body) {
    CloseableHttpClient httpClient = null;
    if (url.startsWith("https")) {
      httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(manager)
          .setUserAgent("SVN/2.0.1").build();
    } else {
      httpClient = HttpClients.custom().setConnectionManager(manager).setUserAgent("SVN/2.0.1").build();
    }
    HttpPost httpPost = new HttpPost(url);
    RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(8000).setConnectTimeout(8000).build();
    httpPost.setConfig(requestConfig);
    httpPost.addHeader("Content-Type", "application/json");
    httpPost.addHeader("Accept", "application/json");
    CloseableHttpResponse httpResponse = null;
    String result = null;
    try {
      httpPost.setEntity(new StringEntity(body));
      httpResponse = httpClient.execute(httpPost);
      if (httpResponse.getStatusLine().getStatusCode() < 300) {
        InputStream in = httpResponse.getEntity().getContent();
        result = org.apache.commons.io.IOUtils.toString(in, "UTF-8");
        in.close();
      } else if (httpResponse.getStatusLine().getStatusCode() == 500) {
        InputStream in = httpResponse.getEntity().getContent();
        result = org.apache.commons.io.IOUtils.toString(in, "UTF-8");
        in.close();
        if (result.contains("{\"message\"=>\"500 Internal Server Error\"}")) {
          result = "{\"result\":3,\"message\":\"FIX /api/v3/internal/subversion no way.\"}";
        } else {
          result = "";
        }
      }
    } catch (Exception e) {
      log.error("request url: {} ", url, e);
    } finally {
      try {
        if (httpResponse != null) {
          httpResponse.close();
        }
      } catch (Exception e) {
        log.error("close failed:", e);
      }
    }
    return result;
  }

  public static AuthorizeResult SubversionAuthorizeEx(@Nullable String useremail, @Nullable String password, int uid,
      @NotNull SubversionPath spi) {
    String pwn = spi.RepoOwner() + "/" + spi.RepoName();
    String url = Parameters.Get().URLPrefix() + "internal/authorize/subversion";
    try {

      String rbody;
      if (useremail != null && password != null) {
        AuthorizeToken token = new AuthorizeToken(pwn, useremail, password, spi.Branch());
        rbody = Resque.mapper.writeValueAsString(token);
      } else {
        AuthorizeUserId token = new AuthorizeUserId(pwn, uid, spi.Branch());
        rbody = Resque.mapper.writeValueAsString(token);
      }

      String rb = HttpAuthorizePost(url, rbody);
      if (rb == null || rb.isEmpty()) {
        return AuthorizeResult.ErrorNew(AuthorizeResult.InternalError, "Authorize server is not responding correctly");
      }
      // ObjectMapper mapper = new ObjectMapper();
      AuthorizeResult sc = Resque.mapper.readValue(rb, AuthorizeResult.class);
      if (sc.ErrorCode() == AuthorizeResult.OK) {
        if (sc.RepositoryPath() == null || sc.RepositoryPath().isEmpty()) {
          // Fallback set path
          sc.RepositoryPath(spi.RepositoryPath());
        }
        sc.UpdateView(sc.RepositoryPath());
      }

      log.info("{}: '{}'' result: {} limit: {} ", pwn, sc.Message(), sc.UID(), DiskSize(sc.Rlimit()));
      return sc;
    } catch (Exception e) {
      log.error("Unknown error", e);
      return AuthorizeResult.ErrorNew(AuthorizeResult.Unknown, "Unknwon error, Please report !");
    }
  }

}